Using Pandas to "applymap" with access to index/column?
<p>I'm currently playing around with a few things involving pandas and I'm wondering what the most effective way to solve the following problem is. Here's a simplified example.</p>

<p>Say I have some data in a data frame:</p>

<pre><code>import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(0,10,size=(10, 4)), columns=['a','b','c','d'], 
                  index=np.random.randint(0,10,size=10))
</code></pre>

<p>This data looks something like this:</p>

<pre><code>   a  b  c  d
1  0  0  9  9
0  2  2  1  7
3  9  3  4  0
2  5  0  9  4
1  7  7  7  2
6  4  4  6  4
1  1  6  0  0
7  8  0  9  3
5  0  0  8  3
4  5  0  2  4
</code></pre>

<p>Now I want to apply some function to each value in the data frame (the one below, for example) and get a data frame back as a resulting output. The tricky part is the function I'm applying depends on the value of the index I am currently at.</p>

<pre><code>def f(cell_val,row_val):
    try:
        return cell_val/row_val
    except ZeroDivisionError:
        return -1
</code></pre>

<p>Normally, if I wanted to apply a function to each individual cell in the data frame, I would just call applymap on "f". Even if I had to pass in a second argument (row_val, in this case), if the argument was a fixed number I could just write a lambda expression such as "lambda x: f(x,i)" where i is the fixed number I wanted. However, my second argument varies depending on the row in the data frame I am currently calling the function from, which means that I can't just use applymap.</p>

<p>How would I go about solving a problem like this efficiently? I can think of a few ways to do this, but none of them feel "right". I could loop through each individual value and replace them one by one, but that seems really awkward and slow. I could also do something like creating a completely separate data frame containing (cell value, row value) tuples and use the built in pandas applymap on my tuple data frame. But that seems pretty hacky and I'm also creating a completely separate data frame as an extra step.</p>

<p>There must be a better solution to this (a fast solution would be appreciated, because there is a possibility that my data frame ends up being very large).</p>

<p>IIUC you can use <a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.div.html" rel="nofollow"><code>div</code></a> with <code>axis=0</code> plus you need to convert the <code>Index</code> object to a <code>Series</code> object using <a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Index.to_series.html" rel="nofollow"><code>to_series</code></a>:</p>

<pre><code>In [121]:
df.div(df.index.to_series(), axis=0).replace(np.inf, -1)

Out[121]:
          a         b         c         d
1  0.000000  0.000000  9.000000  9.000000
0 -1.000000 -1.000000 -1.000000 -1.000000
3  3.000000  1.000000  1.333333  0.000000
2  2.500000  0.000000  4.500000  2.000000
1  7.000000  7.000000  7.000000  2.000000
6  0.666667  0.666667  1.000000  0.666667
1  1.000000  6.000000  0.000000  0.000000
7  1.142857  0.000000  1.285714  0.428571
5  0.000000  0.000000  1.600000  0.600000
4  1.250000  0.000000  0.500000  1.000000
</code></pre>

<p>Additionally as division by zero results in <code>inf</code> you need to call <a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.replace.html#pandas.DataFrame.replace" rel="nofollow"><code>replace</code></a> to replace those rows with <code>-1</code></p>

python python-3.x pandas