Repository: modin-project/modin

Issue:
df.loc[row][column] = value has no effect
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**:

Ubuntu 20.04

- **Modin version** (`modin.__version__`):

0.7.3+165.g9721d46

- **Python version**:

Python 3.8.2

- **Code we can use to reproduce**:

```python
import modin.pandas as pd

df = pd.DataFrame(index=["a", "b", "c"], columns=["col1"])
df.loc["a"]["col1"] = 123
df.loc["b"]["col1"] = 321
df.loc["c"]["col1"] = 444
print(df)
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->

Assigning to `df.loc[row][column]` as no effect and dataframe contents remains all `NaN`.

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->

df.loc[row][column] = value has no effect
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**:

Ubuntu 20.04

- **Modin version** (`modin.__version__`):

0.7.3+165.g9721d46

- **Python version**:

Python 3.8.2

- **Code we can use to reproduce**:

```python
import modin.pandas as pd

df = pd.DataFrame(index=["a", "b", "c"], columns=["col1"])
df.loc["a"]["col1"] = 123
df.loc["b"]["col1"] = 321
df.loc["c"]["col1"] = 444
print(df)
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->

Assigning to `df.loc[row][column]` as no effect and dataframe contents remains all `NaN`.

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->



Discussion:

Resolving patch:
diff --git a/modin/pandas/dataframe.py b/modin/pandas/dataframe.py
--- a/modin/pandas/dataframe.py
+++ b/modin/pandas/dataframe.py
@@ -2329,6 +2329,7 @@ def _getitem_column(self, key):
         ).squeeze(axis=1)
         if isinstance(s, Series):
             s._parent = self
+            s._parent_axis = 1
         return s
 
     def _getitem_array(self, key):
diff --git a/modin/pandas/indexing.py b/modin/pandas/indexing.py
--- a/modin/pandas/indexing.py
+++ b/modin/pandas/indexing.py
@@ -233,6 +233,9 @@ def __getitem__(self, key):
                 "supported, see https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#deprecate-loc-reindex-listlike"
             )
         result = super(_LocIndexer, self).__getitem__(row_lookup, col_lookup, ndim)
+        if isinstance(result, Series):
+            result._parent = self.df
+            result._parent_axis = 0
         # Pandas drops the levels that are in the `loc`, so we have to as well.
         if hasattr(result, "index") and isinstance(result.index, pandas.MultiIndex):
             if (
@@ -341,6 +344,9 @@ def __getitem__(self, key):
 
         row_lookup, col_lookup = self._compute_lookup(row_loc, col_loc)
         result = super(_iLocIndexer, self).__getitem__(row_lookup, col_lookup, ndim)
+        if isinstance(result, Series):
+            result._parent = self.df
+            result._parent_axis = 0
         return result
 
     def __setitem__(self, key, item):
diff --git a/modin/pandas/series.py b/modin/pandas/series.py
--- a/modin/pandas/series.py
+++ b/modin/pandas/series.py
@@ -113,6 +113,12 @@ def _set_name(self, name):
 
     name = property(_get_name, _set_name)
     _parent = None
+    # Parent axis denotes axis that was used to select series in a parent dataframe.
+    # If _parent_axis == 0, then it means that index axis was used via df.loc[row]
+    # indexing operations and assignments should be done to rows of parent.
+    # If _parent_axis == 1 it means that column axis was used via df[column] and assignments
+    # should be done to columns of parent.
+    _parent_axis = 0
 
     def _reduce_dimension(self, query_compiler):
         return query_compiler.to_pandas().squeeze()
@@ -325,7 +331,10 @@ def __setitem__(self, key, value):
         )
         # Propagate changes back to parent so that column in dataframe had the same contents
         if self._parent is not None:
-            self._parent[self.name] = self
+            if self._parent_axis == 0:
+                self._parent.loc[self.name] = self
+            else:
+                self._parent[self.name] = self
 
     def __sub__(self, right):
         return self.sub(right)


Test patch:
diff --git a/modin/pandas/test/test_dataframe.py b/modin/pandas/test/test_dataframe.py
--- a/modin/pandas/test/test_dataframe.py
+++ b/modin/pandas/test/test_dataframe.py
@@ -4456,6 +4456,48 @@ def test_loc_multi_index(self):
             transposed_pandas.loc[transposed_pandas.index[:-2], :],
         )
 
+    def test_loc_assignment(self):
+        modin_df = pd.DataFrame(
+            index=["row1", "row2", "row3"], columns=["col1", "col2"]
+        )
+        pandas_df = pandas.DataFrame(
+            index=["row1", "row2", "row3"], columns=["col1", "col2"]
+        )
+        modin_df.loc["row1"]["col1"] = 11
+        modin_df.loc["row2"]["col1"] = 21
+        modin_df.loc["row3"]["col1"] = 31
+        modin_df.loc["row1"]["col2"] = 12
+        modin_df.loc["row2"]["col2"] = 22
+        modin_df.loc["row3"]["col2"] = 32
+        pandas_df.loc["row1"]["col1"] = 11
+        pandas_df.loc["row2"]["col1"] = 21
+        pandas_df.loc["row3"]["col1"] = 31
+        pandas_df.loc["row1"]["col2"] = 12
+        pandas_df.loc["row2"]["col2"] = 22
+        pandas_df.loc["row3"]["col2"] = 32
+        df_equals(modin_df, pandas_df)
+
+    def test_iloc_assignment(self):
+        modin_df = pd.DataFrame(
+            index=["row1", "row2", "row3"], columns=["col1", "col2"]
+        )
+        pandas_df = pandas.DataFrame(
+            index=["row1", "row2", "row3"], columns=["col1", "col2"]
+        )
+        modin_df.iloc[0]["col1"] = 11
+        modin_df.iloc[1]["col1"] = 21
+        modin_df.iloc[2]["col1"] = 31
+        modin_df.iloc[0]["col2"] = 12
+        modin_df.iloc[1]["col2"] = 22
+        modin_df.iloc[2]["col2"] = 32
+        pandas_df.iloc[0]["col1"] = 11
+        pandas_df.iloc[1]["col1"] = 21
+        pandas_df.iloc[2]["col1"] = 31
+        pandas_df.iloc[0]["col2"] = 12
+        pandas_df.iloc[1]["col2"] = 22
+        pandas_df.iloc[2]["col2"] = 32
+        df_equals(modin_df, pandas_df)
+
     @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
     def test_pop(self, request, data):
         modin_df = pd.DataFrame(data)