mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
PY-82168: Implement inspection and quick fixes for dataframe and series in the conditions
(cherry picked from commit 658f2d5609852b00b7e1011c3841c7266c202686) IJ-MR-168659 GitOrigin-RevId: 66806362570ef564b1495ba5a9afcd32a50859fb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5ad156f695
commit
a8675a61ac
@@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<body>
|
||||
Write your description here.
|
||||
Start the description with a verb in 3rd person singular, like reports, detects, highlights.
|
||||
In the first sentence, briefly explain what exactly the inspection helps you detect.
|
||||
Make sure the sentence is not very long and complicated.
|
||||
<p>
|
||||
The first sentence must be in a dedicated paragraph separated from the rest of the text. This will make the description easier to read.
|
||||
Make sure the description doesn’t just repeat the inspection title.
|
||||
</p>
|
||||
<p>
|
||||
See https://plugins.jetbrains.com/docs/intellij/inspections.html#descriptions for more information.
|
||||
</p>
|
||||
<p>
|
||||
Embed code snippets:
|
||||
</p>
|
||||
<pre><code>
|
||||
// automatically highlighted according to inspection registration 'language' attribute
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p>Text after this comment will only be shown in the settings of the inspection.</p>
|
||||
|
||||
<p>To open related settings directly from the description, add a link with `settings://$` optionally followed by `?$` to pre-select a UI
|
||||
element.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -249,7 +249,10 @@
|
||||
implementationClass="com.jetbrains.python.inspections.PyRelativeImportInspection"/>
|
||||
<localInspection language="Python" shortName="PyPandasSeriesToListInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
|
||||
key="INSP.pandas.series.values.replace.with.tolist" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.jetbrains.python.inspections.PyPandasSeriesToListInspection"/>
|
||||
implementationClass="com.jetbrains.python.inspections.PyPandasSeriesToListInspection" groupKey="INSP.GROUP.pandas"/>
|
||||
<localInspection language="Python" shortName="PyPandasTruthValueIsAmbiguousInspection" suppressId="PyPackages" bundle="messages.PyPsiBundle"
|
||||
key="INSP.pandas.truth.value.is.ambiguous.df" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection" groupKey="INSP.GROUP.pandas"/>
|
||||
|
||||
<listSplitJoinContext language="Python" implementationClass="com.jetbrains.python.codeInsight.editorActions.PyListSplitJoinContext"/>
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Reports ambiguous usage of a pandas <code>DataFrame</code> or <code>Series</code> in a boolean context,
|
||||
such as <code>if</code>, <code>while</code>, or logical expressions.
|
||||
This typically leads to the runtime error:
|
||||
<code>ValueError: The truth value of a DataFrame is ambiguous</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In pandas, expressions like <code>df</code> or <code>df == other</code> do not return a single boolean value,
|
||||
but rather a DataFrame or Series of booleans. Using these in control flow without explicit reduction
|
||||
(e.g., <code>.any()</code>, <code>.all()</code>, or <code>.empty</code>) is ambiguous and will raise an exception.
|
||||
</p>
|
||||
|
||||
<p><b>Example:</b></p>
|
||||
<pre style="font-family: monospace">
|
||||
if df: # ❌ Raises ValueError: The truth value of a DataFrame is ambiguous
|
||||
print("DataFrame exists")
|
||||
|
||||
if not df.empty: # ✅ Checks if DataFrame has any rows
|
||||
print("DataFrame exists")
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
When the quick-fix is applied, the condition is replaced with an appropriate reducer
|
||||
like <code>.any()</code>, <code>.all()</code>, or <code>.empty</code> depending on the context.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1279,8 +1279,13 @@ INSP.class.var.is.not.allowed.here='ClassVar' is not allowed here
|
||||
INSP.class.var.not.a.valid.type=Not a valid type
|
||||
|
||||
# Pandas-Specific inspections and quick fixes
|
||||
INSP.GROUP.pandas=Pandas
|
||||
INSP.pandas.series.values.replace.with.tolist=Method Series.to_list() is recommended
|
||||
QFIX.pandas.series.values.replace.with.tolist=Replace list(Series.values) with Series.to_list()
|
||||
INSP.pandas.truth.value.is.ambiguous.df=The truth value of a DataFrame is ambiguous
|
||||
INSP.pandas.truth.value.is.ambiguous.series=The truth value of a Series is ambiguous
|
||||
QFIX.pandas.truth.value.is.ambiguous.emptiness.check=Replace with explicit emptiness check
|
||||
QFIX.pandas.truth.value.is.ambiguous.none.check=Replace with explicit None check
|
||||
|
||||
# PyTypeParameterListAnnotator
|
||||
type.param.list.annotator.type.parameter.already.defined=Type parameter with name ''{0}'' is already defined in this type parameter list
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.types.PyUnionType
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class PyPandasTruthValueIsAmbiguousInspection : PyInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
return Visitor(holder, PyInspectionVisitor.getContext(session))
|
||||
}
|
||||
|
||||
private class Visitor(private val holder: ProblemsHolder, context: TypeEvalContext) : PyInspectionVisitor(holder, context) {
|
||||
override fun visitPyAssertStatement(node: PyAssertStatement) {
|
||||
super.visitPyAssertStatement(node)
|
||||
val expression = node.getArguments().firstOrNull() ?: return
|
||||
reportProblems(expression)
|
||||
}
|
||||
|
||||
override fun visitPyConditionalStatementPart(node: PyConditionalStatementPart) {
|
||||
super.visitPyConditionalStatementPart(node)
|
||||
val condition = node.condition ?: return
|
||||
reportProblems(condition)
|
||||
}
|
||||
|
||||
private fun reportProblems(expression: PyExpression) {
|
||||
if (expression is PyBinaryExpression) {
|
||||
expression.accept(BinaryExpressionRecursiveVisitor(holder, myTypeEvalContext))
|
||||
}
|
||||
else {
|
||||
expression.reportProblemIfPandasAmbiguousTarget(holder, myTypeEvalContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BinaryExpressionRecursiveVisitor(private val holder: ProblemsHolder, private val context: TypeEvalContext) : PyRecursiveElementVisitor() {
|
||||
override fun visitPyBinaryExpression(node: PyBinaryExpression) {
|
||||
val isIdentity = node.isOperator(PyNames.IS) || node.isOperator("isnot")
|
||||
if (!isIdentity) {
|
||||
val suggestedQuickFixes = node.isOperator(PyNames.AND) || node.isOperator(PyNames.OR) || node.isOperator("==")
|
||||
node.leftExpression?.reportProblemIfPandasAmbiguousTarget(holder, context, suggestedQuickFixes)
|
||||
node.rightExpression?.reportProblemIfPandasAmbiguousTarget(holder, context, suggestedQuickFixes)
|
||||
}
|
||||
super.visitPyBinaryExpression(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PyExpression?.reportProblemIfPandasAmbiguousTarget(holder: ProblemsHolder, typeEvalContext: TypeEvalContext, suggestQuickFixes: Boolean = true) {
|
||||
this ?: return
|
||||
val pandasAmbiguousTarget = when (this) {
|
||||
is PyPrefixExpression -> this.operand.resolvePandasAmbiguousTarget(typeEvalContext)
|
||||
is PyReferenceExpression -> this.resolvePandasAmbiguousTarget(typeEvalContext)
|
||||
else -> null
|
||||
} ?: return
|
||||
val quickFixes = when {
|
||||
!suggestQuickFixes -> emptyArray()
|
||||
this is PyPrefixExpression -> arrayOf(NotPrefixExpressionReplaceWithEmptyCheckQuickFix())
|
||||
this is PyReferenceExpression -> arrayOf(
|
||||
ReferenceExpressionReplaceWithEmptyCheckQuickFix(),
|
||||
ReferenceExpressionReplaceWithExplicitNotNoneCheckQuickFix()
|
||||
)
|
||||
else -> emptyArray()
|
||||
}
|
||||
holder.registerProblem(this, PyPsiBundle.message(pandasAmbiguousTarget.inspectionBundleName),
|
||||
*quickFixes)
|
||||
}
|
||||
|
||||
private enum class PandasAmbiguousTarget(
|
||||
val qualifiedName: String,
|
||||
val inspectionBundleName: String,
|
||||
) {
|
||||
DATA_FRAME(
|
||||
qualifiedName = "pandas.core.frame.DataFrame",
|
||||
inspectionBundleName = "INSP.pandas.truth.value.is.ambiguous.df"
|
||||
),
|
||||
SERIES(
|
||||
qualifiedName = "pandas.core.series.Series",
|
||||
inspectionBundleName = "INSP.pandas.truth.value.is.ambiguous.series"
|
||||
)
|
||||
}
|
||||
|
||||
private fun PyExpression?.resolvePandasAmbiguousTarget(typeEvalContext: TypeEvalContext): PandasAmbiguousTarget? {
|
||||
this ?: return null
|
||||
val type = typeEvalContext.getType(this)
|
||||
val typesToCheck = if (type is PyUnionType) {
|
||||
type.members
|
||||
}
|
||||
else {
|
||||
listOf(type)
|
||||
}
|
||||
return typesToCheck.firstNotNullOfOrNull { memberType ->
|
||||
val qualifiedName = memberType?.declarationElement?.qualifiedName
|
||||
PandasAmbiguousTarget.entries.find { it.qualifiedName == qualifiedName }
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceExpressionReplaceWithEmptyCheckQuickFix() : LocalQuickFix {
|
||||
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.psiElement as? PyReferenceExpression ?: return
|
||||
val factory = PyElementGenerator.getInstance(project)
|
||||
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "not ${element.text}.empty")
|
||||
element.replace(newElement)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceExpressionReplaceWithExplicitNotNoneCheckQuickFix() : LocalQuickFix {
|
||||
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.none.check")
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.psiElement as? PyReferenceExpression ?: return
|
||||
val factory = PyElementGenerator.getInstance(project)
|
||||
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "${element.text} is not None")
|
||||
element.replace(newElement)
|
||||
}
|
||||
}
|
||||
|
||||
private class NotPrefixExpressionReplaceWithEmptyCheckQuickFix() : LocalQuickFix {
|
||||
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.psiElement as? PyPrefixExpression ?: return
|
||||
val operand = element.operand ?: return
|
||||
val factory = PyElementGenerator.getInstance(project)
|
||||
val newElement = factory.createExpressionFromText(LanguageLevel.forElement(element), "${operand.text}.empty")
|
||||
element.replace(newElement)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import Optional
|
||||
import pandas as pd
|
||||
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
other_df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> == None: ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">not df</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> == <warning descr="The truth value of a DataFrame is ambiguous">other_df</warning>: ...
|
||||
|
||||
df_1: Optional[pd.DataFrame] = df
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df_1</warning>: ...
|
||||
|
||||
def e(): ...
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> or e(): ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> and <warning descr="The truth value of a DataFrame is ambiguous">other_df</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> > 5: ...
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">df</warning> and (<warning descr="The truth value of a DataFrame is ambiguous">df</warning> or <warning descr="The truth value of a DataFrame is ambiguous">df</warning>): ...
|
||||
|
||||
while <warning descr="The truth value of a DataFrame is ambiguous">df</warning>: ...
|
||||
|
||||
assert <warning descr="The truth value of a DataFrame is ambiguous">df</warning>
|
||||
|
||||
assert <warning descr="The truth value of a DataFrame is ambiguous">not df</warning>
|
||||
|
||||
if df is None: ...
|
||||
|
||||
if df.empty: ...
|
||||
|
||||
if not df.empty: ...
|
||||
@@ -0,0 +1,2 @@
|
||||
from core.frame import DataFrame
|
||||
from core.series import Series
|
||||
@@ -0,0 +1,18 @@
|
||||
from series import Series
|
||||
|
||||
|
||||
class NDFrame:
|
||||
...
|
||||
|
||||
|
||||
class DataFrame:
|
||||
def __getitem__(self, key):
|
||||
if key == "1":
|
||||
return None
|
||||
if key == "2":
|
||||
return Series()
|
||||
if key == "3":
|
||||
return NDFrame()
|
||||
if key == "4":
|
||||
return DataFrame()
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
class property(object):
|
||||
"""
|
||||
Property attribute.
|
||||
|
||||
fget
|
||||
function to be used for getting an attribute value
|
||||
fset
|
||||
function to be used for setting an attribute value
|
||||
fdel
|
||||
function to be used for del'ing an attribute
|
||||
doc
|
||||
docstring
|
||||
|
||||
Typical use is to define a managed attribute x:
|
||||
|
||||
class C(object):
|
||||
def getx(self): return self._x
|
||||
def setx(self, value): self._x = value
|
||||
def delx(self): del self._x
|
||||
x = property(getx, setx, delx, "I'm the 'x' property.")
|
||||
|
||||
Decorators make defining new properties or modifying existing ones easy:
|
||||
|
||||
class C(object):
|
||||
@property
|
||||
def x(self):
|
||||
"I am the 'x' property."
|
||||
return self._x
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
"""
|
||||
def deleter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different deleter. """
|
||||
pass
|
||||
|
||||
def getter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different getter. """
|
||||
pass
|
||||
|
||||
def setter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different setter. """
|
||||
pass
|
||||
|
||||
def __delete__(self, *args, **kwargs): # real signature unknown
|
||||
""" Delete an attribute of instance. """
|
||||
pass
|
||||
|
||||
def __getattribute__(self, *args, **kwargs): # real signature unknown
|
||||
""" Return getattr(self, name). """
|
||||
pass
|
||||
|
||||
def __get__(self, *args, **kwargs): # real signature unknown
|
||||
""" Return an attribute of instance, which is of type owner. """
|
||||
pass
|
||||
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
|
||||
"""
|
||||
Property attribute.
|
||||
|
||||
fget
|
||||
function to be used for getting an attribute value
|
||||
fset
|
||||
function to be used for setting an attribute value
|
||||
fdel
|
||||
function to be used for del'ing an attribute
|
||||
doc
|
||||
docstring
|
||||
|
||||
Typical use is to define a managed attribute x:
|
||||
|
||||
class C(object):
|
||||
def getx(self): return self._x
|
||||
def setx(self, value): self._x = value
|
||||
def delx(self): del self._x
|
||||
x = property(getx, setx, delx, "I'm the 'x' property.")
|
||||
|
||||
Decorators make defining new properties or modifying existing ones easy:
|
||||
|
||||
class C(object):
|
||||
@property
|
||||
def x(self):
|
||||
"I am the 'x' property."
|
||||
return self._x
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
# (copied from class doc)
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod # known case of __new__
|
||||
def __new__(*args, **kwargs): # real signature unknown
|
||||
""" Create and return a new object. See help(type) for accurate signature. """
|
||||
pass
|
||||
|
||||
def __set__(self, *args, **kwargs): # real signature unknown
|
||||
""" Set an attribute of instance to value. """
|
||||
pass
|
||||
|
||||
fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
|
||||
class IndexOpsMixin():
|
||||
"""
|
||||
Common ops mixin to support a unified interface / docs for Series / Index
|
||||
"""
|
||||
def tolist(self):
|
||||
"""
|
||||
Return a list of the values.
|
||||
|
||||
These are each a scalar type, which is a Python scalar
|
||||
(for str, int, float) or a pandas scalar
|
||||
(for Timestamp/Timedelta/Interval/Period)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
|
||||
See Also
|
||||
--------
|
||||
numpy.ndarray.tolist : Return the array as an a.ndim-levels deep
|
||||
nested list of Python scalars.
|
||||
"""
|
||||
# return self._values.tolist()
|
||||
...
|
||||
|
||||
to_list = tolist
|
||||
|
||||
|
||||
class NDFrame:
|
||||
...
|
||||
|
||||
|
||||
class Series(IndexOpsMixin, NDFrame):
|
||||
"""
|
||||
One-dimensional ndarray with axis labels (including time series).
|
||||
|
||||
Labels need not be unique but must be a hashable type. The object
|
||||
supports both integer- and label-based indexing and provides a host of
|
||||
methods for performing operations involving the index. Statistical
|
||||
methods from ndarray have been overridden to automatically exclude
|
||||
missing data (currently represented as NaN).
|
||||
|
||||
Operations between Series (+, -, /, \\*, \\*\\*) align values based on their
|
||||
associated index values-- they need not be the same length. The result
|
||||
index will be the sorted union of the two indexes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : array-like, Iterable, dict, or scalar value
|
||||
Contains data stored in Series. If data is a dict, argument order is
|
||||
maintained.
|
||||
index : array-like or Index (1d)
|
||||
Values must be hashable and have the same length as `data`.
|
||||
Non-unique index values are allowed. Will default to
|
||||
RangeIndex (0, 1, 2, ..., n) if not provided. If data is dict-like
|
||||
and index is None, then the keys in the data are used as the index. If the
|
||||
index is not None, the resulting Series is reindexed with the index values.
|
||||
dtype : str, numpy.dtype, or ExtensionDtype, optional
|
||||
Data type for the output Series. If not specified, this will be
|
||||
inferred from `data`.
|
||||
See the :ref:`user guide <basics.dtypes>` for more usages.
|
||||
name : str, optional
|
||||
The name to give to the Series.
|
||||
copy : bool, default False
|
||||
Copy input data. Only affects Series or 1d ndarray input. See examples.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Constructing Series from a dictionary with an Index specified
|
||||
|
||||
# >>> d = {'a': 1, 'b': 2, 'c': 3}
|
||||
# >>> ser = pd.Series(data=d, index=['a', 'b', 'c'])
|
||||
# >>> ser
|
||||
a 1
|
||||
b 2
|
||||
c 3
|
||||
dtype: int64
|
||||
|
||||
The keys of the dictionary match with the Index values, hence the Index
|
||||
values have no effect.
|
||||
|
||||
# >>> d = {'a': 1, 'b': 2, 'c': 3}
|
||||
# >>> ser = pd.Series(data=d, index=['x', 'y', 'z'])
|
||||
# >>> ser
|
||||
x NaN
|
||||
y NaN
|
||||
z NaN
|
||||
dtype: float64
|
||||
|
||||
Note that the Index is first build with the keys from the dictionary.
|
||||
After this the Series is reindexed with the given Index values, hence we
|
||||
get all NaN as a result.
|
||||
|
||||
Constructing Series from a list with `copy=False`.
|
||||
|
||||
# >>> r = [1, 2]
|
||||
# >>> ser = pd.Series(r, copy=False)
|
||||
# >>> ser.iloc[0] = 999
|
||||
# >>> r
|
||||
[1, 2]
|
||||
# >>> ser
|
||||
0 999
|
||||
1 2
|
||||
dtype: int64
|
||||
|
||||
Due to input data type the Series has a `copy` of
|
||||
the original data even though `copy=False`, so
|
||||
the data is unchanged.
|
||||
|
||||
Constructing Series from a 1d ndarray with `copy=False`.
|
||||
|
||||
# >>> r = np.array([1, 2])
|
||||
# >>> ser = pd.Series(r, copy=False)
|
||||
# >>> ser.iloc[0] = 999
|
||||
# >>> r
|
||||
array([999, 2])
|
||||
# >>> ser
|
||||
0 999
|
||||
1 2
|
||||
dtype: int64
|
||||
|
||||
Due to input data type the Series has a `view` on
|
||||
the original data, so
|
||||
the data is changed as well.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
data=None,
|
||||
index=None,
|
||||
dtype = None,
|
||||
name=None,
|
||||
copy = False,
|
||||
fastpath = False,
|
||||
):
|
||||
...
|
||||
|
||||
@property
|
||||
def values(self):
|
||||
"""
|
||||
Return Series as ndarray or ndarray-like depending on the dtype.
|
||||
|
||||
.. warning::
|
||||
|
||||
We recommend using :attr:`Series.array` or
|
||||
:meth:`Series.to_numpy`, depending on whether you need
|
||||
a reference to the underlying data or a NumPy array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray or ndarray-like
|
||||
|
||||
See Also
|
||||
--------
|
||||
Series.array : Reference to the underlying data.
|
||||
Series.to_numpy : A NumPy array representing the underlying data.
|
||||
|
||||
Examples
|
||||
--------
|
||||
# >>> pd.Series([1, 2, 3]).values
|
||||
array([1, 2, 3])
|
||||
|
||||
# >>> pd.Series(list('aabc')).values
|
||||
array(['a', 'a', 'b', 'c'], dtype=object)
|
||||
|
||||
# >>> pd.Series(list('aabc')).astype('category').values
|
||||
['a', 'a', 'b', 'c']
|
||||
Categories (3, object): ['a', 'b', 'c']
|
||||
|
||||
Timezone aware datetime data is converted to UTC:
|
||||
|
||||
# >>> pd.Series(pd.date_range('20130101', periods=3,
|
||||
... tz='US/Eastern')).values
|
||||
array(['2013-01-01T05:00:00.000000000',
|
||||
'2013-01-02T05:00:00.000000000',
|
||||
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
|
||||
"""
|
||||
# return self._mgr.external_values()
|
||||
...
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import Optional
|
||||
import pandas as pd
|
||||
|
||||
series = pd.Series([1, 2, 3])
|
||||
other_series = pd.Series([4, 5, 6])
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> == None: ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">not series</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> == <warning descr="The truth value of a Series is ambiguous">other_series</warning>: ...
|
||||
|
||||
series_1: Optional[pd.Series] = series
|
||||
if <warning descr="The truth value of a Series is ambiguous">series_1</warning>: ...
|
||||
|
||||
def e(): ...
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> or e(): ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> and <warning descr="The truth value of a Series is ambiguous">other_series</warning>: ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> > 5: ...
|
||||
|
||||
if <warning descr="The truth value of a Series is ambiguous">series</warning> and (<warning descr="The truth value of a Series is ambiguous">series</warning> or <warning descr="The truth value of a Series is ambiguous">series</warning>): ...
|
||||
|
||||
while <warning descr="The truth value of a Series is ambiguous">series</warning>: ...
|
||||
|
||||
assert <warning descr="The truth value of a Series is ambiguous">series</warning>
|
||||
|
||||
assert <warning descr="The truth value of a Series is ambiguous">not series</warning>
|
||||
|
||||
if series is None: ...
|
||||
|
||||
if series.empty: ...
|
||||
|
||||
if not series.empty: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">d<caret>f</warning>:
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if not df.empty:
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">not d<caret>f</warning>:
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if df.empty:
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if <warning descr="The truth value of a DataFrame is ambiguous">d<caret>f</warning>:
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
import pandas as pd
|
||||
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
|
||||
|
||||
if df is not None:
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
from series import Series
|
||||
from core.frame import DataFrame
|
||||
@@ -0,0 +1,18 @@
|
||||
from series import Series
|
||||
|
||||
|
||||
class NDFrame:
|
||||
...
|
||||
|
||||
|
||||
class DataFrame:
|
||||
def __getitem__(self, key):
|
||||
if key == "1":
|
||||
return None
|
||||
if key == "2":
|
||||
return Series()
|
||||
if key == "3":
|
||||
return NDFrame()
|
||||
if key == "4":
|
||||
return DataFrame()
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
class property(object):
|
||||
"""
|
||||
Property attribute.
|
||||
|
||||
fget
|
||||
function to be used for getting an attribute value
|
||||
fset
|
||||
function to be used for setting an attribute value
|
||||
fdel
|
||||
function to be used for del'ing an attribute
|
||||
doc
|
||||
docstring
|
||||
|
||||
Typical use is to define a managed attribute x:
|
||||
|
||||
class C(object):
|
||||
def getx(self): return self._x
|
||||
def setx(self, value): self._x = value
|
||||
def delx(self): del self._x
|
||||
x = property(getx, setx, delx, "I'm the 'x' property.")
|
||||
|
||||
Decorators make defining new properties or modifying existing ones easy:
|
||||
|
||||
class C(object):
|
||||
@property
|
||||
def x(self):
|
||||
"I am the 'x' property."
|
||||
return self._x
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
"""
|
||||
def deleter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different deleter. """
|
||||
pass
|
||||
|
||||
def getter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different getter. """
|
||||
pass
|
||||
|
||||
def setter(self, *args, **kwargs): # real signature unknown
|
||||
""" Descriptor to obtain a copy of the property with a different setter. """
|
||||
pass
|
||||
|
||||
def __delete__(self, *args, **kwargs): # real signature unknown
|
||||
""" Delete an attribute of instance. """
|
||||
pass
|
||||
|
||||
def __getattribute__(self, *args, **kwargs): # real signature unknown
|
||||
""" Return getattr(self, name). """
|
||||
pass
|
||||
|
||||
def __get__(self, *args, **kwargs): # real signature unknown
|
||||
""" Return an attribute of instance, which is of type owner. """
|
||||
pass
|
||||
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
|
||||
"""
|
||||
Property attribute.
|
||||
|
||||
fget
|
||||
function to be used for getting an attribute value
|
||||
fset
|
||||
function to be used for setting an attribute value
|
||||
fdel
|
||||
function to be used for del'ing an attribute
|
||||
doc
|
||||
docstring
|
||||
|
||||
Typical use is to define a managed attribute x:
|
||||
|
||||
class C(object):
|
||||
def getx(self): return self._x
|
||||
def setx(self, value): self._x = value
|
||||
def delx(self): del self._x
|
||||
x = property(getx, setx, delx, "I'm the 'x' property.")
|
||||
|
||||
Decorators make defining new properties or modifying existing ones easy:
|
||||
|
||||
class C(object):
|
||||
@property
|
||||
def x(self):
|
||||
"I am the 'x' property."
|
||||
return self._x
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
# (copied from class doc)
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod # known case of __new__
|
||||
def __new__(*args, **kwargs): # real signature unknown
|
||||
""" Create and return a new object. See help(type) for accurate signature. """
|
||||
pass
|
||||
|
||||
def __set__(self, *args, **kwargs): # real signature unknown
|
||||
""" Set an attribute of instance to value. """
|
||||
pass
|
||||
|
||||
fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
|
||||
|
||||
|
||||
class IndexOpsMixin():
|
||||
"""
|
||||
Common ops mixin to support a unified interface / docs for Series / Index
|
||||
"""
|
||||
def tolist(self):
|
||||
"""
|
||||
Return a list of the values.
|
||||
|
||||
These are each a scalar type, which is a Python scalar
|
||||
(for str, int, float) or a pandas scalar
|
||||
(for Timestamp/Timedelta/Interval/Period)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
|
||||
See Also
|
||||
--------
|
||||
numpy.ndarray.tolist : Return the array as an a.ndim-levels deep
|
||||
nested list of Python scalars.
|
||||
"""
|
||||
# return self._values.tolist()
|
||||
...
|
||||
|
||||
to_list = tolist
|
||||
|
||||
|
||||
class NDFrame:
|
||||
...
|
||||
|
||||
|
||||
class Series(IndexOpsMixin, NDFrame):
|
||||
"""
|
||||
One-dimensional ndarray with axis labels (including time series).
|
||||
|
||||
Labels need not be unique but must be a hashable type. The object
|
||||
supports both integer- and label-based indexing and provides a host of
|
||||
methods for performing operations involving the index. Statistical
|
||||
methods from ndarray have been overridden to automatically exclude
|
||||
missing data (currently represented as NaN).
|
||||
|
||||
Operations between Series (+, -, /, \\*, \\*\\*) align values based on their
|
||||
associated index values-- they need not be the same length. The result
|
||||
index will be the sorted union of the two indexes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : array-like, Iterable, dict, or scalar value
|
||||
Contains data stored in Series. If data is a dict, argument order is
|
||||
maintained.
|
||||
index : array-like or Index (1d)
|
||||
Values must be hashable and have the same length as `data`.
|
||||
Non-unique index values are allowed. Will default to
|
||||
RangeIndex (0, 1, 2, ..., n) if not provided. If data is dict-like
|
||||
and index is None, then the keys in the data are used as the index. If the
|
||||
index is not None, the resulting Series is reindexed with the index values.
|
||||
dtype : str, numpy.dtype, or ExtensionDtype, optional
|
||||
Data type for the output Series. If not specified, this will be
|
||||
inferred from `data`.
|
||||
See the :ref:`user guide <basics.dtypes>` for more usages.
|
||||
name : str, optional
|
||||
The name to give to the Series.
|
||||
copy : bool, default False
|
||||
Copy input data. Only affects Series or 1d ndarray input. See examples.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Constructing Series from a dictionary with an Index specified
|
||||
|
||||
# >>> d = {'a': 1, 'b': 2, 'c': 3}
|
||||
# >>> ser = pd.Series(data=d, index=['a', 'b', 'c'])
|
||||
# >>> ser
|
||||
a 1
|
||||
b 2
|
||||
c 3
|
||||
dtype: int64
|
||||
|
||||
The keys of the dictionary match with the Index values, hence the Index
|
||||
values have no effect.
|
||||
|
||||
# >>> d = {'a': 1, 'b': 2, 'c': 3}
|
||||
# >>> ser = pd.Series(data=d, index=['x', 'y', 'z'])
|
||||
# >>> ser
|
||||
x NaN
|
||||
y NaN
|
||||
z NaN
|
||||
dtype: float64
|
||||
|
||||
Note that the Index is first build with the keys from the dictionary.
|
||||
After this the Series is reindexed with the given Index values, hence we
|
||||
get all NaN as a result.
|
||||
|
||||
Constructing Series from a list with `copy=False`.
|
||||
|
||||
# >>> r = [1, 2]
|
||||
# >>> ser = pd.Series(r, copy=False)
|
||||
# >>> ser.iloc[0] = 999
|
||||
# >>> r
|
||||
[1, 2]
|
||||
# >>> ser
|
||||
0 999
|
||||
1 2
|
||||
dtype: int64
|
||||
|
||||
Due to input data type the Series has a `copy` of
|
||||
the original data even though `copy=False`, so
|
||||
the data is unchanged.
|
||||
|
||||
Constructing Series from a 1d ndarray with `copy=False`.
|
||||
|
||||
# >>> r = np.array([1, 2])
|
||||
# >>> ser = pd.Series(r, copy=False)
|
||||
# >>> ser.iloc[0] = 999
|
||||
# >>> r
|
||||
array([999, 2])
|
||||
# >>> ser
|
||||
0 999
|
||||
1 2
|
||||
dtype: int64
|
||||
|
||||
Due to input data type the Series has a `view` on
|
||||
the original data, so
|
||||
the data is changed as well.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
data=None,
|
||||
index=None,
|
||||
dtype = None,
|
||||
name=None,
|
||||
copy = False,
|
||||
fastpath = False,
|
||||
):
|
||||
...
|
||||
|
||||
@property
|
||||
def values(self):
|
||||
"""
|
||||
Return Series as ndarray or ndarray-like depending on the dtype.
|
||||
|
||||
.. warning::
|
||||
|
||||
We recommend using :attr:`Series.array` or
|
||||
:meth:`Series.to_numpy`, depending on whether you need
|
||||
a reference to the underlying data or a NumPy array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray or ndarray-like
|
||||
|
||||
See Also
|
||||
--------
|
||||
Series.array : Reference to the underlying data.
|
||||
Series.to_numpy : A NumPy array representing the underlying data.
|
||||
|
||||
Examples
|
||||
--------
|
||||
# >>> pd.Series([1, 2, 3]).values
|
||||
array([1, 2, 3])
|
||||
|
||||
# >>> pd.Series(list('aabc')).values
|
||||
array(['a', 'a', 'b', 'c'], dtype=object)
|
||||
|
||||
# >>> pd.Series(list('aabc')).astype('category').values
|
||||
['a', 'a', 'b', 'c']
|
||||
Categories (3, object): ['a', 'b', 'c']
|
||||
|
||||
Timezone aware datetime data is converted to UTC:
|
||||
|
||||
# >>> pd.Series(pd.date_range('20130101', periods=3,
|
||||
... tz='US/Eastern')).values
|
||||
array(['2013-01-01T05:00:00.000000000',
|
||||
'2013-01-02T05:00:00.000000000',
|
||||
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
|
||||
"""
|
||||
# return self._mgr.external_values()
|
||||
...
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.inspections
|
||||
|
||||
import com.jetbrains.python.fixtures.PyInspectionTestCase
|
||||
|
||||
class PyPandasTruthValueIsAmbiguousInspectionTest : PyInspectionTestCase() {
|
||||
override fun getInspectionClass(): Class<out PyInspection?> {
|
||||
return PyPandasTruthValueIsAmbiguousInspection::class.java
|
||||
}
|
||||
|
||||
fun testDataFrame() {
|
||||
myFixture.copyDirectoryToProject("inspections/PyPandasTruthValueIsAmbiguousInspection/pandas", "pandas")
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testSeries() {
|
||||
myFixture.copyDirectoryToProject("inspections/PyPandasTruthValueIsAmbiguousInspection/pandas", "pandas")
|
||||
doTest()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.quickFixes
|
||||
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.PyQuickFixTestCase
|
||||
import com.jetbrains.python.inspections.PyPandasTruthValueIsAmbiguousInspection
|
||||
|
||||
class PyPandasTruthValueIsAmbiguousQuickFixTest : PyQuickFixTestCase() {
|
||||
private val emptinessQuickFixName = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.emptiness.check")
|
||||
private val noneQuickFixName = PyPsiBundle.message("QFIX.pandas.truth.value.is.ambiguous.none.check")
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.copyDirectoryToProject("", "")
|
||||
}
|
||||
|
||||
fun testEmptyCheck() {
|
||||
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, emptinessQuickFixName)
|
||||
}
|
||||
|
||||
fun testNotNone() {
|
||||
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, noneQuickFixName)
|
||||
}
|
||||
|
||||
|
||||
fun testNegativeEmptyCheck() {
|
||||
doQuickFixTest(PyPandasTruthValueIsAmbiguousInspection::class.java, emptinessQuickFixName)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user